package com.mj.haier.calc.service;

import java.math.BigDecimal;
import java.time.Instant;
import java.util.*;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.TypedQuery;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.mj.haier.calc.domain.AzCarsGroupSettlement;
import com.mj.haier.calc.domain.AzCarsteamGroupSettlement;
import com.mj.haier.calc.domain.AzEncourage;
import com.mj.haier.calc.domain.AzEncourageCash;
import com.mj.haier.calc.domain.AzEncourageGroupSettlement;
import com.mj.haier.calc.domain.AzEorderSettlementProcess;
import com.mj.haier.calc.domain.AzEorderTitle;
import com.mj.haier.calc.domain.AzEorderTitleError;
import com.mj.haier.calc.domain.AzFinSettleBill;
import com.mj.haier.calc.domain.AzFinSettleCars;
import com.mj.haier.calc.domain.AzFinSettleCarsteam;
import com.mj.haier.calc.domain.BdGvsItemconfig;
import com.mj.haier.calc.domain.BdSwitcher;
import com.mj.haier.calc.domain.CarsTeamInfo;
import com.mj.haier.calc.repository.AzEorderTitleErrorRepository;
import com.mj.haier.calc.repository.AzEorderTitleRepository;
import com.mj.haier.calc.repository.AzFinSettleBillRepository;
import com.mj.haier.calc.repository.AzFinSettleCarsRepository;
import com.mj.haier.calc.repository.AzFinSettleCarsteamRepository;
import com.mj.haier.calc.servlet.ApplicationFilterChain;
import com.mj.haier.calc.servlet.Filter;
import com.mj.haier.calc.servlet.Request;
import com.mj.haier.calc.servlet.Response;
import com.mj.haier.calc.servlet.Servlet;
import com.mj.haier.calc.servlet.impl.AllComplianceFilter;
import com.mj.haier.calc.servlet.impl.CarsFilter;
import com.mj.haier.calc.servlet.impl.CarsteamFilter;
import com.mj.haier.calc.servlet.impl.DefaultRequest;
import com.mj.haier.calc.servlet.impl.DefaultResponse;
import com.mj.haier.calc.servlet.impl.LastLevelFilter;
import com.mj.haier.calc.servlet.impl.PercentFilter;
import com.mj.haier.calc.servlet.impl.UpdateServletAzEorderSettlmentProcess;
import com.mj.haier.calc.repository.AzEncourageCashRepository;
import com.mj.haier.calc.repository.AzEncourageRepository;
import com.mj.haier.calc.repository.AzEorderSettlementProcessRepository;
import com.mj.haier.calc.sharding.generator.ShardingKeyGenerator;
import com.mj.haier.calc.client.FinClient;
import com.mj.haier.calc.client.RrskxClient;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StopWatch;

/**
 * 安装单业务逻辑服务
 * @author ycm
 */
@Service
@Transactional
public class AzEorderService {

    private final Logger log = LoggerFactory.getLogger(AzEorderService.class);

    private final AzEorderTitleRepository azEorderTitleRepository;

    private final AzEorderTitleErrorRepository azEorderTitleErrorRepository;

    private final AzEorderSettlementProcessRepository azEorderSettlementProcessRepository;

    private final RrskxClient rrskxClient;

    private final FinClient finClient;

    private final AzFinSettleBillRepository azFinSettleBillRepository;

    private final AzFinSettleCarsteamRepository azFinSettleCarsteamRepository;

    private final AzFinSettleCarsRepository azFinSettleCarsRepository;

    private List<Filter> filters;
    private Servlet servlet;

    private final AzEncourageRepository azEncourageRepository;

    private final AzEncourageCashRepository azEncourageCashRepository;

    public AzEorderService(AzEorderTitleRepository azEorderTitleRepository, AzEorderTitleErrorRepository azEorderTitleErrorRepository,
        AzEorderSettlementProcessRepository azEorderSettlementProcessRepository, RrskxClient rrskxClient, FinClient finClient,
        AzFinSettleBillRepository azFinSettleBillRepository, AzFinSettleCarsteamRepository azFinSettleCarsteamRepository, AzFinSettleCarsRepository azFinSettleCarsRepository,
        AzEncourageRepository azEncourageRepository, AzEncourageCashRepository azEncourageCashRepository){
        this.azEorderTitleRepository = azEorderTitleRepository;
        this.azEorderTitleErrorRepository = azEorderTitleErrorRepository;
        this.azEorderSettlementProcessRepository = azEorderSettlementProcessRepository;
        this.rrskxClient = rrskxClient;
        this.finClient = finClient;
        this.azFinSettleBillRepository = azFinSettleBillRepository;
        this.azFinSettleCarsteamRepository = azFinSettleCarsteamRepository;
        this.azFinSettleCarsRepository = azFinSettleCarsRepository;
        this.azEncourageRepository = azEncourageRepository;
        this.azEncourageCashRepository = azEncourageCashRepository;

        initFilterChain();
    }

    private void initFilterChain(){

        //1, 初始化filters
        filters = new ArrayList<>();
        filters.add(new CarsteamFilter(rrskxClient));
        filters.add(new CarsFilter(rrskxClient));
        filters.add(new PercentFilter());
        filters.add(new LastLevelFilter(finClient));
        filters.add(new AllComplianceFilter());

        //2, 初始化Servlet
        servlet = new UpdateServletAzEorderSettlmentProcess(azEorderSettlementProcessRepository);
    }

    /**
     * 第一步： 同步到安装订单表， 错误的订单放入错误记录表
     *
     * @param iterable
     */
    public void syncBatchAzEorderTitle(Iterable<AzEorderTitle> iterable){

        log.debug("syncBatchAzEorderTitle start, Iterable<AzEorderTitle>");
        iterable.forEach(azEorderTitle -> {

            log.debug("azEorderTitle: {}", azEorderTitle.toString());
            if(StringUtils.isNotBlank(azEorderTitle.getOrderCode())){
                // 如何已存在在安装订单表，保存到错误记录表
               if(azEorderTitleRepository.existsByTradeCodeAndOrderCode(azEorderTitle.getTradeCode(), azEorderTitle.getOrderCode())){
                    AzEorderTitleError azEorderTitleError = new AzEorderTitleError()
                            .orderCode(azEorderTitle.getOrderCode())
                            .tradeCode(azEorderTitle.getTradeCode())
                            .serverCloseTime(azEorderTitle.getServerCloseTime())
                            .eorderTitleJson(JSON.toJSONString(azEorderTitle))
                            .isDeal("0")
                            .gmtCreate(Instant.now())
                            .createUser("syncBatchAzEorderTitle func");
                    log.debug("azEorderTitleError: {}", azEorderTitleError.toString());
                    azEorderTitleErrorRepository.save(azEorderTitleError);
               }else{
                    azEorderTitle.gmtCreate(Instant.now()).createUser("syncBatchAzEorderTitle func")
                        .isPublished(false);
                    // azEorderTitleRepository.save(azEorderTitle);
                    entityManager.persist(azEorderTitle);
               }
               // 更新快线安装单状态为 2
               rrskxClient.updateAzOrderTitleFinSettle(azEorderTitle.getTradeCode(), azEorderTitle.getOrderCode());
            }
        });
        entityManager.flush();
        entityManager.clear();

    }


    /**
     * 第二步:  错误记录表处理后，强制更新到安装订单表， 以便后续结算
     *
     * @param iterable
     */
    public void backBatchAzEorderTitleError2AzEorderTitle(Iterable<AzEorderTitleError> iterable){

        log.debug("backBatchAzEorderTitleError2AzEorderTitle start, Iterable<AzEorderTitleError>");
        iterable.forEach(azEorderTitleError -> {

            log.debug("azEorderTitleError: {}", azEorderTitleError.toString());
            if(StringUtils.isNotBlank(azEorderTitleError.getEorderTitleJson())){
                AzEorderTitle azEorderTitle = JSONObject.parseObject(azEorderTitleError.getEorderTitleJson(), AzEorderTitle.class);
                log.debug("azEorderTitle: {}", azEorderTitle.toString());
                azEorderTitle.gmtCreate(Instant.now()).createUser("backBatchAzEorderTitleError2AzEorderTitle func")
                    .gmtModified(Instant.now()).modifiedUser("backBatchAzEorderTitleError2AzEorderTitle func")
                    .isPublished(false);
                azEorderTitleRepository.save(azEorderTitle);
                // 更新处理状态，时间
                azEorderTitleError.isDeal("2")
                    .dealTime(Instant.now())
                    .dealMsg("强制更新处理完成")
                    .gmtModified(Instant.now())
                    .modifiedUser("backBatchAzEorderTitleError2AzEorderTitle func");
                log.debug("update azEorderTitleError: {}", azEorderTitleError.toString());
                azEorderTitleErrorRepository.save(azEorderTitleError);
            }
        });
    }


    /**
     * 第三步: 把当月需结算的源安装订单， 同步到临时表, 临时表要创建，按照月度字段【settlement_cycle】分表
     * 同步完成， 变更【is_published】= 0: 发布;
     *
     * @param iterable
     * @param settlementCycle  月度
     */

    public void syncBatchAzEorderSettlementProcess(Iterable<AzEorderTitle> iterable, String settlementCycle){

        log.debug("syncBatchAzEorderSettlementProcess start, Iterable<AzEorderTitle> and settlementCycle : {}", settlementCycle);

        iterable.forEach(azEorderTitle -> {
            log.debug("azEorderTitle: {}", azEorderTitle.toString());
            try {
                AzEorderSettlementProcess azEorderSettlementProcess = new AzEorderSettlementProcess();
                BeanUtils.copyProperties(azEorderTitle, azEorderSettlementProcess);
                azEorderSettlementProcess.setId(ShardingKeyGenerator.keyGenerator().longValue());
                azEorderSettlementProcess.setSettlementCycle(settlementCycle);
                azEorderSettlementProcess.setCarsTeamSettleBatchId("0");
                azEorderSettlementProcess.setCarsSettleBatchId("0");
                azEorderSettlementProcess.setCreateUser("syncBatchAzEorderSettlementProcess func");
                azEorderSettlementProcess.setGmtCreate(Instant.now());

                // azEorderSettlementProcessRepository.save(azEorderSettlementProcess);
                entityManager.persist(azEorderSettlementProcess);
                //保存成功，变更【is_published】= 1: 发布
                azEorderTitle.isPublished(true)
                    .modifiedUser("syncBatchAzEorderSettlementProcess func")
                    .gmtModified(Instant.now());
                azEorderTitleRepository.updateIsPublished(azEorderTitle.isIsPublished(), azEorderTitle.getGmtModified(),
                    azEorderTitle.getModifiedUser(), azEorderTitle.getTradeCode(), azEorderTitle.getOrderCode());
            } catch (Exception e) {
                log.debug("当前插入安装单临时表错误： {}", e.getMessage());
            }
        });
        entityManager.flush();
        entityManager.clear();
    }

    /**
     * 第四步: 合规性检验，责任链模式
     *
     * @param iterable
     */
    public void complianceCheckAzEorderSettlementProcess(Iterable<AzEorderSettlementProcess> iterable){

        log.debug("complianceCheckAzEorderSettlementProcess,  Iterable<AzEorderSettlementProcess> start");

        //3, 初始化责任链
        ApplicationFilterChain chain = new ApplicationFilterChain(filters.toArray(new Filter[filters.size()]), servlet);

        iterable.forEach(azEorderSettlementProcess -> {
            log.debug("azEorderSettlementProcess: {}", azEorderSettlementProcess.toString());
            Request req = new DefaultRequest().object(azEorderSettlementProcess);
            Response res = new DefaultResponse();
            try {
                chain.doFilter(req, res);
            } catch (Exception e) {
                log.debug("complianceCheckAzEorderSettlementProcess 执行异常，错误: {}", e.getMessage());
            }
        });
    }

    /**
     * 第五步： 不合規的订单信息在源订单表中删除
     *
     * @param iterable
     */
    public void deleteNotComplianceAzEorderTitle(Iterable<AzEorderSettlementProcess> iterable){

        log.debug("deleteNotComplianceAzEorderTitle,  Iterable<AzEorderSettlementProcess> start");
        iterable.forEach(azEorderSettlementProcess -> {

            log.debug("azEorderSettlementProcess: {}", azEorderSettlementProcess.toString());
            azEorderTitleRepository.deleteByTradeCodeAndOrderCode(azEorderSettlementProcess.getTradeCode(),azEorderSettlementProcess.getOrderCode());
            azEorderSettlementProcess.modifiedUser("deleteNotComplianceAzEorderTitle func")
                .gmtModified(Instant.now())
                .allCompliance("d")
                .remark("因不合规在源订单表中刪除");
            azEorderSettlementProcessRepository.save(azEorderSettlementProcess);
        });
    }

    @PersistenceContext
    private EntityManager entityManager;
    /**
     * 第六步: 分组合并生成结算单(分车队与车辆), 分发相应的结算明细
     *      车队
     * @param settlementCycle
     * @param tradeCode
     * @param beginDate
     * @param endDate
     */
    public void settlementAzFinSettleCarsteam(String settlementCycle, String tradeCode, Instant beginDate, Instant endDate){

        log.debug("settlementAzFinSettleCarsteam, settlementCycle: {}, tradeCode: {}", settlementCycle, tradeCode);
        List<AzCarsteamGroupSettlement> carsteamGroups = azEorderSettlementProcessRepository.findCarsteamGroup(settlementCycle, tradeCode);

        carsteamGroups.forEach(carsteamGroup -> {

            String carsTeamId = carsteamGroup.getCarsTeamId();
            String carsteamTradeCode = carsteamGroup.getTradeCode();
            String sourceNameCode = carsteamGroup.getSourceNameCode();
            String settleBatchId = "AZ1JSPC" + ShardingKeyGenerator.keyGenerator().longValue();
            List<AzEorderSettlementProcess> processes = azEorderSettlementProcessRepository.findAllBySettlementCycleAndTradeCodeAndSourceNameCodeAndCarsTeamIdAndAllCompliance(
                settlementCycle, carsteamTradeCode, sourceNameCode, carsTeamId, "0"
            );
            this.saveSettlementAzFinSettleCarsteam(settlementCycle, beginDate, endDate, carsTeamId, carsteamTradeCode, sourceNameCode, settleBatchId, processes);

        });
    }


    /**
     * 缩小事务保存范围，处理当前批次号下的车队结算单，结算明细
     *
     * @param settleBatchId
     * @param processes
     * @param settlementCycle
     * @param carsteamTradeCode
     * @param beginDate
     * @param endDate
     * @param sourceNameCode
     */
    private void saveSettlementAzFinSettleCarsteam(String settlementCycle, Instant beginDate, Instant endDate, String carsTeamId, String carsteamTradeCode, String sourceNameCode, String settleBatchId, List<AzEorderSettlementProcess> processes){
        BigDecimal settleAmounts = new BigDecimal("0");
        if (processes.size() > 0) {

            // StopWatch stopWatch = new StopWatch();
            // stopWatch.start("for.each");
            Long keyGenerator = ShardingKeyGenerator.keyGenerator().longValue();
            for (int i = 0; i < processes.size(); i++) {
                AzEorderSettlementProcess process = processes.get(i);
                AzFinSettleBill bill = new AzFinSettleBill()
                    .id(keyGenerator + i)
                    .settleId("AZ1B" + process.getOrderCode())
                    .settleBatchId(settleBatchId)
                    .orderCode(process.getOrderCode())
                    .settleItem("1")
                    .tradeCode(carsteamTradeCode)
                    .settleObjectId(carsTeamId)
                    .settleObjectTel(process.getCarsTeamTel())
                    .carsTeamId(carsTeamId)
                    .carsTeamName(process.getCarsTeamName())
                    .productNo(process.getProductNo())
                    .productName(process.getProductName())
                    .productModelNo(process.getProductModelNo())
                    .productModelName(process.getProductModelName())
                    .productSerNo(process.getProductSerNo())
                    .productSerDesc(process.getProductSerDesc())
                    .sourceNameCode(sourceNameCode)
                    .sourceName(process.getSourceName())
                    .haierCloseStatus(process.getHaierCloseStatus())
                    .haierCloseTime(process.getHaierCloseTime())
                    .serverCloseStatus(process.getServerCloseStatus())
                    .serverCloseTime(process.getServerCloseTime())
                    .settleOperateCost(process.getCarsTeamOperateCost())
                    .settleServiceTypeCost(process.getCarsTeamServiceTypeCost())
                    .settleTrafficCost(process.getCarsTeamTrafficCost())
                    .settleMeterialCost(process.getCarsTeamMeterialCost())
                    .settleSubidyCost(process.getCarsTeamSubidyCost())
                    .settleCompensatoryCost(process.getCarsTeamCompensatoryCost())
                    .settleRewardCost(process.getCarsTeamRewardCost())
                    .settleFeeTotalCost(process.getCarsTeamTotalFee())
                    .companyCode(process.getCompanyCode())
                    .lastLevel(process.getLastLevel())
                    .lastLevelName(process.getLastLevelName())
                    .gmtCreate(Instant.now())
                    .add1(process.getAdd1())
                    .createUser("settlementAzFinSettleCarsteam func");
                settleAmounts = settleAmounts.add(new BigDecimal(process.getCarsTeamTotalFee()));
                entityManager.persist(bill);
                process.carsTeamSettleBatchId(settleBatchId).modifiedUser("settlementAzFinSettleCarsteam func").gmtModified(Instant.now());
                //更新批次号到所有明细
                entityManager.merge(process);
            }
            entityManager.flush();
            entityManager.clear();
            // stopWatch.stop();
            // 插入当前车队结算单
            // stopWatch.start("create.settleCarsteam");
            AzFinSettleCarsteam settleCarsteam = new AzFinSettleCarsteam()
                .settleBatchId(settleBatchId)
                .beginDate(beginDate).endDate(endDate)
                .detailCount(processes.size())
                .account(settlementCycle)
                .settleAmounts(settleAmounts.toPlainString())
                .settleEncourageAmounts("0").unsettleNegativeAmounts("0")
                .actualAmounts(settleAmounts.toPlainString())
                .carsTeamId(carsTeamId).carsTeamName(processes.get(0).getCarsTeamName()).carsTeamCode(processes.get(0).getCarsTeamCode()).netpoints(processes.get(0).getServerInfoCode())
                .tradeCode(carsteamTradeCode)
                .feeTypeCode("carsteam").feeTypeName("车队")
                .sourceNameCode(sourceNameCode).sourceName(processes.get(0).getSourceName())
                .corporation(processes.get(0).getCompanyCode())
                .lastLevel(processes.get(0).getLastLevel()).lastLevelName(processes.get(0).getLastLevelName()).add1(processes.get(0).getAdd1())
                .gmtCreate(Instant.now()).createUser("settlementAzFinSettleCarsteam func");

            azFinSettleCarsteamRepository.save(settleCarsteam);
            // stopWatch.stop();
            // log.debug("settlementAzFinSettleCarsteam: 耗时{}", stopWatch.prettyPrint());
        }
    }


     /**
     * 第六步: 分组合并生成结算单(分车队与车辆), 分发相应的结算明细
     *      车辆
     * @param settlementCycle
     * @param tradeCode
     * @param beginDate
     * @param endDate
     */
    public void settlementAzFinSettleCars(String settlementCycle, String tradeCode, Instant beginDate, Instant endDate){

        log.debug("settlementAzFinSettleCars, settlementCycle: {}, tradeCode: {}", settlementCycle, tradeCode);
        List<AzCarsGroupSettlement> carsGroups = azEorderSettlementProcessRepository.findCarsGroup(settlementCycle, tradeCode);
        carsGroups.forEach(carsGroup -> {

            String carsId = carsGroup.getCarsId();
            String carsTradeCode = carsGroup.getTradeCode();
            String sourceNameCode = carsGroup.getSourceNameCode();

            String settleBatchId = "AZ0JSPC" + ShardingKeyGenerator.keyGenerator().longValue();
            List<AzEorderSettlementProcess> processes = azEorderSettlementProcessRepository.findAllBySettlementCycleAndTradeCodeAndSourceNameCodeAndCarsIdAndAllCompliance(
                settlementCycle, carsTradeCode, sourceNameCode, carsId, "0"
            );

            BigDecimal settleAmounts = new BigDecimal("0");

            if(processes.size() > 0){
                for (AzEorderSettlementProcess process : processes) {

                    AzFinSettleBill bill = new AzFinSettleBill()
                        .id(ShardingKeyGenerator.keyGenerator().longValue())
                        .settleId("AZ0B" + process.getOrderCode())
                        .settleBatchId(settleBatchId)
                        .orderCode(process.getOrderCode())
                        .settleItem("0")
                        .tradeCode(carsTradeCode)
                        .settleObjectId(carsId)
                        .settleObjectTel(process.getDriverTel())
                        .carsTeamId(process.getCarsTeamId())
                        .carsTeamName(process.getCarsTeamName())
                        .productNo(process.getProductNo())
                        .productName(process.getProductName())
                        .productModelNo(process.getProductModelNo())
                        .productModelName(process.getProductModelName())
                        .productSerNo(process.getProductSerNo())
                        .productSerDesc(process.getProductSerDesc())
                        .sourceNameCode(sourceNameCode)
                        .sourceName(process.getSourceName())
                        .haierCloseStatus(process.getHaierCloseStatus())
                        .haierCloseTime(process.getHaierCloseTime())
                        .serverCloseStatus(process.getServerCloseStatus())
                        .serverCloseTime(process.getServerCloseTime())
                        .settleOperateCost(process.getCarsOperateCost())
                        .settleServiceTypeCost(process.getCarsServiceTypeCost())
                        .settleTrafficCost(process.getCarsTrafficCost())
                        .settleMeterialCost(process.getCarsMeterialCost())
                        .settleSubidyCost(process.getCarsSubidyCost())
                        .settleCompensatoryCost(process.getCarsCompensatoryCost())
                        .settleRewardCost(process.getCarsRewardCost())
                        .settleFeeTotalCost(process.getCarsTotalFee())
                        .companyCode(process.getCompanyCode())
                        .lastLevel(process.getLastLevel())
                        .lastLevelName(process.getLastLevelName())
                        .gmtCreate(Instant.now())
                        .add1(process.getAdd1())
                        .createUser("settlementAzFinSettleCars func");
                    settleAmounts = settleAmounts.add(new BigDecimal(process.getCarsTotalFee()));
                    azFinSettleBillRepository.save(bill);

                    process.carsSettleBatchId(settleBatchId).modifiedUser("settlementAzFinSettleCars func").gmtModified(Instant.now());
                }
                // 更新批次号到所有明细
                azEorderSettlementProcessRepository.saveAll(processes);
                // 插入当前车辆结算单
                AzFinSettleCars settleCars = new AzFinSettleCars()
                    .settleBatchId(settleBatchId)
                    .beginDate(beginDate).endDate(endDate)
                    .detailCount(processes.size())
                    .account(settlementCycle)
                    .settleAmounts(settleAmounts.toPlainString())
                    .settleEncourageAmounts("0").unsettleNegativeAmounts("0")
                    .actualAmounts(settleAmounts.toPlainString())
                    .carsTeamId(processes.get(0).getCarsTeamId()).carsTeamName(processes.get(0).getCarsTeamName()).carsTeamCode(processes.get(0).getCarsTeamCode()).netpoints(processes.get(0).getServerInfoCode())
                    .tradeCode(carsTradeCode)
                    .carsId(carsId).carsNumber(processes.get(0).getCarsNumber()).driverTel(processes.get(0).getDriverTel())
                    .feeTypeCode("cars").feeTypeName("车辆")
                    .sourceNameCode(sourceNameCode).sourceName(processes.get(0).getSourceName())
                    .corporation(processes.get(0).getCompanyCode())
                    .lastLevel(processes.get(0).getLastLevel()).lastLevelName(processes.get(0).getLastLevelName()).add1(processes.get(0).getAdd1())
                    .gmtCreate(Instant.now()).createUser("settlementAzFinSettleCars func");

                azFinSettleCarsRepository.save(settleCars);
            }

        });
    }

    public List<AzFinSettleCarsteam> getCarsteamResultList(String account, String initial, String target, Integer scale){
         //字段越少，查询越快
         String findAllNeedCashSql = "select new AzFinSettleCarsteam(id, settleBatchId, settleAmounts, settleEncourageAmounts, unsettleNegativeAmounts, actualAmounts, carsTeamId, sourceNameCode) from AzFinSettleCarsteam a where a.account = ?1 and a.add2 = ?2";
         TypedQuery<AzFinSettleCarsteam> query = entityManager.createQuery(findAllNeedCashSql, AzFinSettleCarsteam.class);
         query.setParameter(1, account);
         query.setParameter(2, "null".equals(initial) ? null : initial);
         query.setFirstResult(0);
         query.setMaxResults(scale);
         return query.getResultList() == null? Collections.<AzFinSettleCarsteam>emptyList(): query.getResultList();
    }

    /**
     * 激励兑现当车队
     *
     * @param account
     * @param isForSourceNameCode
     */
	public void encourageCashAzFinSettleCarsteam(List<AzFinSettleCarsteam> carsteams, String isForSourceNameCode, String target) {

        if(null == carsteams || carsteams.isEmpty()){
            return;
        }
        log.debug("兑现的车队结算单条数为: {}", carsteams.size());
        for (AzFinSettleCarsteam  carsteam : carsteams) {
            StringBuilder sb = new StringBuilder("from AzEncourageCash a")
                .append(" ").append("where a.settleBatchId = '0'")
                .append(" ").append("and a.encourageObject = ").append("'").append(carsteam.getCarsTeamId()).append("'");

            if("0".equals(isForSourceNameCode)){
                sb.append(" ").append("and a.sourceNameCode = ").append("'").append(carsteam.getSourceNameCode()).append("'");
            }
            sb.append(" ").append("order by  a.outstanding desc");

            TypedQuery<AzEncourageCash> encourageQuery = entityManager.createQuery(sb.toString(), AzEncourageCash.class);
            List<AzEncourageCash> cashs = encourageQuery.getResultList();
            log.debug("符合当前兑现对象: {}，的激励明细条数: {}", carsteam.getCarsTeamId(), cashs.size());
            if(null == cashs || cashs.isEmpty()){
                String updateAdd2 = "UPDATE az_fin_settle_carsteam SET add_2 = ? ,gmt_modified = ? WHERE id = ?";
                Query nativeQuery = entityManager.createNativeQuery(updateAdd2)
                    .setParameter(1, target)
                    .setParameter(2, Instant.now())
                    .setParameter(3, carsteam.getId());
                nativeQuery.executeUpdate();
                continue;
            }
            // 开始兑现激励
            CashEncourage cashEncourage = new CashEncourage(new BigDecimal(null == carsteam.getActualAmounts()? "0": carsteam.getActualAmounts())
                ,new BigDecimal(null == carsteam.getSettleEncourageAmounts()?"0":carsteam.getSettleEncourageAmounts()));
            foreachCashEncourage(cashs, cashEncourage, carsteam.getSettleBatchId(), "carsteam");

            // 更新网点最终激励的合计，实际应付金额
            carsteam.settleEncourageAmounts(cashEncourage.getSettleEncourageAmounts().toPlainString())
                .actualAmounts(cashEncourage.getActualAmounts().toPlainString())
                .modifiedUser("encourageCash...carsteams func").gmtModified(Instant.now());
            String updateEncourageCash = "UPDATE az_fin_settle_carsteam SET settle_encourage_amounts = ?, actual_amounts = ?, gmt_modified = ?, modified_user = ?, add_2 = ?  WHERE id = ?";
            Query nativeQuery = entityManager.createNativeQuery(updateEncourageCash)
                .setParameter(1, carsteam.getSettleEncourageAmounts())
                .setParameter(2, carsteam.getActualAmounts())
                .setParameter(3, carsteam.getGmtModified())
                .setParameter(4, carsteam.getModifiedUser())
                .setParameter(5, target)
                .setParameter(6, carsteam.getId());
            nativeQuery.executeUpdate();
//            azFinSettleCarsteamRepository.updateEncourageCash(carsteam);

        }
        entityManager.flush();
        entityManager.clear();
    }

    /**
     * 激励安装每条进行匹配兑现
     *
     * @param cashs
     * @param cashEncourage
     * @param settleBatchId
     */
    private void foreachCashEncourage(List<AzEncourageCash> cashs, CashEncourage cashEncourage, String settleBatchId, String feeType) {

        for(AzEncourageCash cash: cashs){

            // 如果实付等于0， 则表示无法进行兑现，退出, 不判断第一步，如果实付为0， 但有正激励，则进行第二步判断
            if(cashEncourage.getActualAmounts().compareTo(BigDecimal.ZERO) == 0){
                return;
            }
            // 待兑现的激励金额
            BigDecimal outstanding = new BigDecimal(cash.getOutstanding());
            // 兑现的金额
            BigDecimal payCash = (cashEncourage.getActualAmounts().add(outstanding)).compareTo(BigDecimal.ZERO) >= 0 ? outstanding : BigDecimal.ZERO.subtract(cashEncourage.getActualAmounts());
            // 未兑现的金额
            BigDecimal notPayCash = outstanding.subtract(payCash);
            cashEncourage.setActualAmounts(cashEncourage.getActualAmounts().add(payCash));
            cashEncourage.setSettleEncourageAmounts(cashEncourage.getSettleEncourageAmounts().add(payCash));


            // 更新激励兑现信息
            cash.settleBatchId(settleBatchId).payCash(payCash.toPlainString()).outstanding(notPayCash.toPlainString()).gmtModified(Instant.now()).modifiedUser("foreachCashEncourage func");
            azEncourageCashRepository.saveAndFlush(cash);
            // 更新激励主信息，兑现状态
            String updateSettleState = "UPDATE az_encourage SET settle_state = ?, gmt_modified = ?, modified_user = ? WHERE id = ? ";
            String udpateSecoundSettleState = "UPDATE az_encourage SET second_settle_state = ?, gmt_modified = ?,  modified_user = ? WHERE id = ?";
            Query query = null;
            if("carsteam".equals(feeType)){
                query = entityManager.createNativeQuery(updateSettleState)
                    .setParameter(1, notPayCash.compareTo(BigDecimal.ZERO) == 0 ? "1" : "2")
                    .setParameter(2, Instant.now())
                    .setParameter(3, "foreachCashEncourage func")
                    .setParameter(4, cash.getParentId());
            }else if("cars".equals(feeType)){
                query = entityManager.createNativeQuery(udpateSecoundSettleState)
                    .setParameter(1, notPayCash.compareTo(BigDecimal.ZERO) == 0 ? "1" : "2")
                    .setParameter(2, Instant.now())
                    .setParameter(3, "foreachCashEncourage func")
                    .setParameter(4, cash.getParentId());
            }
            query.executeUpdate();
            // 未兑现完成的激励，重新生成一条激励单信息
            if(notPayCash.compareTo(BigDecimal.ZERO) != 0){

                AzEncourageCash newAzEncourageCash = new AzEncourageCash()
                    .id(cash.getId() + 1)
                    .parentId(cash.getParentId()).encourageId(cash.getEncourageId())
                    .settleBatchId("0")
                    .encourageObject(cash.getEncourageObject()).encourageSecondObject(cash.getEncourageSecondObject())
                    .payCash("0").outstanding(notPayCash.toPlainString()).incentiveToCdk("0")
                    .encourageBudget(cash.getEncourageBudget()).sourceSystem(cash.getSourceSystem())
                    .sourceNameCode(cash.getSourceNameCode()).remark("负激励没有全部兑换完成")
                    .gmtCreate(Instant.now()).createUser("foreachCashEncourage func");
                azEncourageCashRepository.save(newAzEncourageCash);

            }
        }
    }

    class CashEncourage {

        private BigDecimal actualAmounts;
        private BigDecimal settleEncourageAmounts;

        public CashEncourage(BigDecimal actualAmounts, BigDecimal settleEncourageAmounts){
            this.actualAmounts = actualAmounts;
            this.settleEncourageAmounts = settleEncourageAmounts;
        }

        /**
         * @return the actualAmounts
         */
        public BigDecimal getActualAmounts() {
            return actualAmounts;
        }

        /**
         * @param actualAmounts the actualAmounts to set
         */
        public void setActualAmounts(BigDecimal actualAmounts) {
            this.actualAmounts = actualAmounts;
        }

        /**
         * @return the settleEncourageAmounts
         */
        public BigDecimal getSettleEncourageAmounts() {
            return settleEncourageAmounts;
        }

        /**
         * @param settleEncourageAmounts the settleEncourageAmounts to set
         */
        public void setSettleEncourageAmounts(BigDecimal settleEncourageAmounts) {
            this.settleEncourageAmounts = settleEncourageAmounts;
        }




    }

    /**
     * 激励兑现当车辆
     *
     * @param account
     * @param isForSourceNameCode
     */
	public void encourageCashAzFinSettleCars(String account, String isForSourceNameCode) {

        //字段越少，查询越快
        String findAllNeedCashSql = "select new AzFinSettleCars(id, settleBatchId, settleAmounts, settleEncourageAmounts, unsettleNegativeAmounts, actualAmounts, carsId, sourceNameCode) from AzFinSettleCars a where a.account = ?1 and a.actualAmounts > 0";
        TypedQuery<AzFinSettleCars> query = entityManager.createQuery(findAllNeedCashSql, AzFinSettleCars.class);
        query.setParameter(1, account);
        List<AzFinSettleCars> cars = query.getResultList();
        if(null == cars || cars.isEmpty()){
            return;
        }
        for (AzFinSettleCars  car : cars) {
            StringBuilder sb = new StringBuilder("from AzEncourageCash a")
                .append(" ").append("where a.settleBatchId = '0'")
                .append(" ").append("and a.encourageSecondObject = ").append("'").append(car.getCarsId()).append("'");

            if("0".equals(isForSourceNameCode)){
                sb.append(" ").append("and a.sourceNameCode = ").append("'").append(car.getSourceNameCode()).append("'");
            }
            sb.append(" ").append("order by  a.outstanding desc");

            TypedQuery<AzEncourageCash> encourageQuery = entityManager.createQuery(sb.toString(), AzEncourageCash.class);
            List<AzEncourageCash> cashs = encourageQuery.getResultList();
            if(null == cashs || cashs.isEmpty()){
                continue;
            }

            // 开始兑现激励
            CashEncourage cashEncourage = new CashEncourage(new BigDecimal(null == car.getActualAmounts()? "0": car.getActualAmounts())
                ,new BigDecimal(null == car.getSettleEncourageAmounts()?"0":car.getSettleEncourageAmounts()));
            foreachCashEncourage(cashs, cashEncourage, car.getSettleBatchId(), "cars");

            // 更新网点最终激励的合计，实际应付金额
            car.settleEncourageAmounts(cashEncourage.getSettleEncourageAmounts().toPlainString())
                .actualAmounts(cashEncourage.getActualAmounts().toPlainString())
                .modifiedUser("encourageCash...cars func").gmtModified(Instant.now());

            String updateEncourageCash = "UPDATE az_fin_settle_cars SET settle_encourage_amounts = ?, actual_amounts = ?, gmt_modified = ?, modified_user = ? WHERE id = ?";
            Query nativeQuery = entityManager.createNativeQuery(updateEncourageCash)
                .setParameter(1, car.getSettleEncourageAmounts())
                .setParameter(2, car.getActualAmounts())
                .setParameter(3, car.getGmtModified())
                .setParameter(4, car.getModifiedUser())
                .setParameter(5, car.getId());
            nativeQuery.executeUpdate();
//            azFinSettleCarsRepository.updateEncourageCash(car);
        }
	}

    /**
     * 京东正激励生成车队结算单信息
     * @param settlementCycle
     * @param beginDate
     * @param endDate
     */
	public void encourage4AzFinSettleCarsteam(String settlementCycle, Instant beginDate, Instant endDate) {

        List<AzEncourageGroupSettlement> settlements = azEncourageRepository.findAzEncourageGroup();
        if( null == settlements || settlements.size() == 0){
            return;
        }
        for(AzEncourageGroupSettlement settlement: settlements){

            String netpoint = settlement.getNetpoint();
            String settleBatchId = "AZ1JSPC" + ShardingKeyGenerator.keyGenerator().longValue();
            List<CarsTeamInfo> carsTeaminfos = rrskxClient.searchCarsTeamInfosByNetpoint(netpoint);
            if(carsTeaminfos.size() == 0){
                continue;
            }
            String carsTeamId = carsTeaminfos.get(0).getCarsTeamId();
            String carsTeamName = carsTeaminfos.get(0).getCarsTeamName();
            String carsTeamCode = carsTeaminfos.get(0).getCarsTeamCode();

            String tradeCode = settlement.getTradeCode();
            BdSwitcher switcher = rrskxClient.getBdSwitcher(tradeCode);
            if("默认值".equals(switcher.getSwitcherName())){
                continue;
            }

            String sourceNameCode = settlement.getSourceNameCode();
            BdGvsItemconfig bdGvsItemconfig = finClient.getBdGvsItemconfig(sourceNameCode);
            if(null == bdGvsItemconfig.getDepartment()){
                continue;
            }

            AzFinSettleCarsteam settleCarsteam = new AzFinSettleCarsteam()
                .settleBatchId(settleBatchId)
                .beginDate(beginDate).endDate(endDate)
                .detailCount(0)
                .account(settlementCycle)
                .settleAmounts("0").settleEncourageAmounts("0").unsettleNegativeAmounts("0").actualAmounts("0")
                .carsTeamId(carsTeamId).carsTeamName(carsTeamName).carsTeamCode(carsTeamCode).netpoints(netpoint)
                .tradeCode(tradeCode)
                .feeTypeCode("carsteam").feeTypeName("车队")
                .sourceNameCode(sourceNameCode).sourceName(bdGvsItemconfig.getSourceName())
                .corporation(settlement.getCompanyCode())
                .lastLevel(settlement.getDepartment()).lastLevelName(settlement.getDepartmentName()).add1(switcher.getSwitcherName())
                .gmtCreate(Instant.now()).createUser("encourage4AzFinSettleCarsteam func");

            azFinSettleCarsteamRepository.save(settleCarsteam);
        }
	}

}
